昨天在 CommonJS 的介紹中最後提到 require
在載入模組是同步的,當模組還沒被載入完成前,後續的操作都會被阻斷。不知道你有沒有想過,為什麼 server side 同步載入模組可以被接受,但 client side 就會有問題呢?
因為在古早時期,大多時候對 server side 來說,所需要載入的模組通常都被放在同一個檔案系統中,因此載入速度基本上不成問題,且有機會做一些預先加載的快取處理。
上面指的是 ESM 尚未出現前,在 ESM 標準出現後,Node.js 在 v13.2.0 後能穩定支援 ESM,這也讓非同步模組載入能在 Node.js 中做到。
但對瀏覽器環境就不一樣了,大多時候你需要載入的模組可能來自各處的遠端伺服器,中間的網路延遲時間不可控,如果每載入一個模組就卡住後面的頁面加載、渲染等工作,體驗上就會很差,所以通常在瀏覽器端會期待能做到非同步加載。
因爲上述這個問題,CommonJS 社群在想要將模組化標準推向瀏覽器這件事出現了意見分歧,詳細的演進可見這篇文章,簡單紀錄的話就是分成幾派:
此標準的主要概念是「依賴前置」,可能有點難懂,可以看以下這個範例:
// 用 define 來定義模組
define(
// 1. module id
'homePageModule',
// 2. dependency array
['jquery', 'lib/math'],
// 3. callback function
function ($, math) {
return {
render: function () {
$('body').text('Hello AMD!' + math.add(1, 2));
}
};
});
// 載入模組
require(['homePageModule'], function (homePageModule) {
homePageModule.render();
});
參考上面的註解說明可以這樣理解一個模組載入的過程:
require
去載入 homePageModule
這個模組。jquery
、lib/math
,來進行非同步載入。但 CommonJS 社群並不接受這樣的做法,主要有兩個不認同的地方:
define
來做模組定義define(['module1', 'module2', 'module3'], function(module1, module2, module3) {
...
// module3 可能只在特定條件下才會用到
if (someCondition) {
module3.doSomething();
}
});
因此後來 AMD 社群自己另外獨立出去發展。儘管不被原本的社群認同,RequireJS 與 AMD 的概念仍被廣泛接受影響了一些函式庫的模組化標準,甚至也蓋過了其他有深厚技術底蘊的門派如 BravoJS、FlyScript 聲量。
今天簡單點到了在模組化歷史中,曾經為了將模組化標準搬上瀏覽器而有了這段百家爭鳴的現象,一直以來我沒有搞很懂的非同步模組載入這件事為什麼在瀏覽器上會有問題,也在這次比較深入理解 AMD 的歷史後豁然開朗,希望我的筆記們也有幫助到正在閱讀的你。明天也終於快進到「前端工具」這個重點了,敬請期待!